'abucollisionmap.monkey by Belimoth

Strict
Import abutilemap
Import abuphysicscomponent



Interface ICollisionMap
	Method Trace:AABB(aabb:PhysicsComponent, direction:Int)
End



Class AABB		'move this to abusizecomponent? or abuphysicscomponent? or abubasics?
	Field x:Int, y:Int, w:Int, h:Int
	
	Method New(x:Int, y:Int, w:Int, h:Int)
		Self.x = x
		Self.y = y
		Self.w = w
		Self.h = h
	End
	
	Method GetEdge:Int(direction:Int)
		Select direction
			Case DIRECTION_NORTH
				Return y
			Case DIRECTION_EAST
				Return x + w - 1
			Case DIRECTION_SOUTH
				Return y + h - 1
			Case DIRECTION_WEST
				Return x
			Default
		End
		Return 0
	End
	
	Method Render:Void()
		DrawRect(x, y, w, h)
	End
End



Class AbuCollisionMap Extends TileMapBase<Bool> Implements ICollisionMap
	Field mesh:AABB[][][]
	Field bumper:AABB[]
	
	Method New(cellWidth:Int, cellHeight:Int, width:Int, height:Int)
		Self.cellWidth = cellWidth
		Self.cellHeight = cellHeight
		Self.width = width
		Self.height = height
		data = data.Resize(width)
		mesh = mesh.Resize(width)
		For Local x := 0 Until width
			data[x] = data[x].Resize(height)
			mesh[x] = mesh[x].Resize(height)
			For Local y := 0 Until height
				mesh[x][y] = mesh[x][y].Resize(4)
			Next
		Next
		
		bumper = New AABB[4]
		bumper[DIRECTION_NORTH] = New AABB(-1, -1, width * cellWidth + 2, 1)
		bumper[DIRECTION_EAST] = New AABB(width * cellWidth, -1, 1, height * cellHeight + 2)
		bumper[DIRECTION_SOUTH] = New AABB(-1, height * cellHeight, width * cellWidth + 2, 1)
		bumper[DIRECTION_WEST] = New AABB(-1, -1, 1, height * cellHeight + 2)
	End
	
	Method Set:Void(x:Int, y:Int, value:Bool)
		If (x >= 0) And (x < width) And (y >= 0) And (y < height)
			data[x][y] = value
			For Local xTemp:Int = 0 Until width
				RecalculateMesh(xTemp, y)
			Next
			For Local yTemp:Int = 0 Until height
				RecalculateMesh(x, yTemp)
			Next
		Endif
	End
	
	Method RecalculateMesh:Void(x:Int, y:Int)
		If data[x][y] = 0
			For Local n := Eachin DIRECTIONS_CARDINAL
				mesh[x][y][n] = Null
			Next
			Return
		Endif
		
		Local x0:Int, x1:Int, y0:Int, y1:Int
		For Local n := Eachin DIRECTIONS_CARDINAL
			Select n
				Case DIRECTION_NORTH, DIRECTION_SOUTH
					'left
					x0 = x
					While x0 > 0
						x0 -= 1
						If data[x0][y] = 0
							x0 +=1
							Exit
						Endif
					Wend
					'right
					x1 = x
					While x1 < width - 1
						x1 += 1
						If data[x1][y] = 0
							x1 -= 1
							Exit
						Endif
					Wend
					mesh[x][y][n] = New AABB(x0 * cellWidth, y * cellHeight, (x1 - x0 + 1) * cellWidth, cellHeight)
				Case DIRECTION_EAST, DIRECTION_WEST
					'up
					y0 = y
					While y0 > 0
						y0 -= 1
						If data[x][y0] = 0
							y0 += 1
							Exit
						Endif
					Wend
					'down
					y1 = y
					While y1 < height - 1
						y1 += 1
						If data[x][y1] = 0
							y1 -= 1
							Exit
						Endif
					Wend
					mesh[x][y][n] = New AABB(x * cellWidth, y0 * cellHeight, cellWidth, (y1 - y0 + 1) * cellHeight)
				Default
			End
		Next
	End
	
	Method Trace:AABB(aabb:PhysicsComponent, direction:Int)
		Local x0:Int = MapX(aabb._x)
		Local y0:Int = MapY(aabb._y)
		Local x1:Int = MapX(aabb._x + aabb._w - 1)
		Local y1:Int = MapY(aabb._y + aabb._h - 1)
		
		x0 = Max(x0, 0)
		y0 = Max(y0, 0)
		x1 = Min(x1, width - 1)
		y1 = Min(y1, height - 1)
		
		Local champ:AABB
		
		Select direction
			Case DIRECTION_NORTH
				For Local y := y0 - 1 To 0 Step -1
				For Local x := x0 To x1
					If Get(x, y) = True
						Local challenger:AABB = mesh[x][y][DIRECTION_NORTH]
						If champ = Null
							champ = challenger
						Else
							If challenger.GetEdge(DIRECTION_SOUTH) > champ.GetEdge(DIRECTION_SOUTH)
								champ = challenger
							Endif
						Endif
						'TODO Exit For loops
					Endif
				Next
				Next
			Case DIRECTION_SOUTH
				For Local y := y1 + 1 Until height Step 1
				For Local x := x0 To x1
					If Get(x, y) = True
						Local challenger:AABB = mesh[x][y][DIRECTION_SOUTH]
						If champ = Null
							champ = challenger
						Else
							If challenger.GetEdge(DIRECTION_NORTH) < champ.GetEdge(DIRECTION_NORTH)			'notice different comparison
								champ = challenger
							Endif
						Endif
					Endif
				Next
				Next
			Case DIRECTION_WEST
				For Local x := x0 - 1 To 0 Step -1
				For Local y := y0 To y1
					If Get(x, y) = True
						Local challenger:AABB = mesh[x][y][DIRECTION_WEST]
						If champ = Null
							champ = challenger
						Else
							If challenger.GetEdge(DIRECTION_EAST) > champ.GetEdge(DIRECTION_EAST)
								champ = challenger
							Endif
						Endif
						'TODO Exit For loops
					Endif
				Next
				Next
			Case DIRECTION_EAST
				For Local x := x1 + 1 Until width Step 1
				For Local y := y0 To y1
					If Get(x, y) = True
						Local challenger:AABB = mesh[x][y][DIRECTION_EAST]
						If champ = Null
							champ = challenger
						Else
							If challenger.GetEdge(DIRECTION_WEST) < champ.GetEdge(DIRECTION_WEST)			'notice different comparison
								champ = challenger
							Endif
						Endif
					Endif
				Next
				Next
			Default
		End
		
		If champ = Null Then champ = bumper[direction]
		Return champ
	End		'TODO have AABB parameter instead of PhysicsComponent
	
	Method Render:Void()		'debug only
		SetColor 255, 255, 255
		For Local n:Int = 0 To 3
			bumper[n].Render()
		Next
		
		SetColor 128, 128, 128
		For Local x:Int = 0 Until width
		For Local y:Int = 0 until height
			If Get(x, y) Then DrawRect(x * cellWidth, y * cellHeight, cellWidth, cellHeight)
		Next
		Next
	End
End